React(16) - Generic function component


Posted by TempuraEngineer on 2023-05-27

目錄


Generic component是什麼

純Typescript有泛型的功能,用Typescript寫的Reactcomponent也可以和泛型結合

這個技巧相當地實用,在一些UI library內也會看到


泛型的React component幾個優點

  1. 利於複用
  2. 維持型別彈性同時也避免過於彈性,故能避開潛在的型別錯誤


Generic component怎麼寫

假設有個組件叫GenericSelect,它是一個包裝select而成的組件

其選項會有多個,選項的文字一定是string,值則可能是string或者number

當onChange被處發時,它的上層組件可以透過onSelect拿到選擇的值

開始前可以先想一下組件可能怎麼被使用

      <GeniricSelect
        options={genderOptions}
        onSelect={(selectedGender) => {
          // 直接接收值
          setGender(selectedGender);
        }}
      />

      <GeniricSelect
        options={genderOptions}
        onSelect={({ target }) => { 
          // 接收event,再取出target.value
          setGender(target.value);
        }}
      />

那麼先來定義props

  interface GeniricSelectProps<T> {
    options: { 
      text: string; 
      // 值可能是string或number,所以用泛型
      // 至於為什麼不寫string | number,因為這樣型別過於廣泛,所以用泛型來限縮範圍
      value: T 
    }[]; 
    onSelect: (value: T) => void;
  }

然後寫組件本體

這時會發現有type error,這是因為e.target.value會被預設認為是string即便input type="number"也一樣

這時有兩個方式

1.一種是直接把value的型別改為unknown,然後在使用GenericSelect的地方,傳function給onSelect時,內部加上type guard

interface GeniricSelectProps<T> {
  options: { text: string; value: T }[];
  onSelect: (value: unknown) => void;
}

const GeniricSelect = <P extends string | number>({
  options,
  onSelect
}: GeniricSelectProps<P>) => {
  return (
    <select
      name=""
      id=""
      style={{ padding: 8 }}
      onChange={(e: ChangeEvent<HTMLSelectElement>) => onSelect(e.target.value)}
    >
      {options?.map(({ text, value }) => (
        <option value={value} key={value}>
          {text}
        </option>
      ))}
    </select>
  );
};

2.使用intersection(&)來讓target.value的型別不再只限於string

interface GeniricSelectProps<T> {
  options: { text: string; value: T }[];
  onSelect: (
    e: ChangeEvent<HTMLSelectElement> & { target: { value: T } }
  ) => void;
}

const GeniricSelect = <P extends string | number>({
  options,
  onSelect
}: GeniricSelectProps<P>) => {
  return (
    <select name="" id="" style={{ padding: 8 }} onChange={onSelect}>
      {options?.map(({ text, value }) => (
        <option value={value} key={value}>
          {text}
        </option>
      ))}
    </select>
  );
};


參考資料

Create a React / TypeScript Generic Component
How to set type for event.target.value?


#React #TypeScript #generic #Function component







Related Posts

「新手問題」為什麼我不能下載 npm 的套件?

「新手問題」為什麼我不能下載 npm 的套件?

1731. The Number of Employees Which Report to Each Employee

1731. The Number of Employees Which Report to Each Employee

Some relative page about the "dependent types"

Some relative page about the "dependent types"


Comments